🎓 JavaScript Classes & Modules

Học cách tổ chức code hiệu quả với Classes và Modules trong JavaScript

📋 Tổng quan về Classes & Modules

🌟 Lập trình hướng đối tượng (OOP) trong JavaScript

🧠 Khái niệm cơ bản về OOP:

Object-Oriented Programming (OOP) là paradigm lập trình dựa trên khái niệm "objects" - các thực thể chứa data (properties) và code (methods). JavaScript hỗ trợ OOP thông qua:

  • Objects: Đối tượng chứa state và behavior
  • Classes: Template/blueprint để tạo objects
  • Instances: Các objects được tạo từ class
  • Methods: Functions thuộc về object
  • Properties: Variables thuộc về object

�️ 4 Nguyên lý cốt lõi của OOP

1. 🛡️ Encapsulation (Đóng gói)

Ý nghĩa: Gói gọn data và methods liên quan trong một object, ẩn chi tiết implementation.

Lợi ích:

  • Bảo vệ data khỏi truy cập trực tiếp
  • Kiểm soát cách data được thay đổi
  • Dễ maintain và debug

2. 🧬 Inheritance (Kế thừa)

Ý nghĩa: Class con có thể kế thừa properties và methods từ class cha.

Lợi ích:

  • Tái sử dụng code hiệu quả
  • Tạo hierarchy của classes
  • Mở rộng functionality

3. 🎭 Polymorphism (Đa hình)

Ý nghĩa: Cùng một method có thể hoạt động khác nhau ở các class khác nhau.

Lợi ích:

  • Code linh hoạt và mở rộng
  • Interface thống nhất
  • Override methods

4. �🎯 Abstraction (Trừu tượng)

Ý nghĩa: Ẩn complexity, chỉ expose những gì cần thiết.

Lợi ích:

  • Đơn giản hóa interface
  • Tách biệt what và how
  • Dễ sử dụng và hiểu

🔄 Evolution của JavaScript OOP

JavaScript OOP Evolution
javascript
// 🕰️ ES5 và trước đó - Function Constructors
function PersonES5(name, age) {
  this.name = name;
  this.age = age;
}

PersonES5.prototype.greet = function() {
  return "Hello, I'm " + this.name;
};

var person1 = new PersonES5("John", 25);

// 🚀 ES6+ (2015) - Classes (Syntactic Sugar)
class PersonES6 {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

const person2 = new PersonES6("Jane", 30);

// 🔬 ES2022 - Private Fields
class PersonModern {
  #age;  // Private field
  
  constructor(name, age) {
    this.name = name;
    this.#age = age;
  }
  
  get age() { return this.#age; }
  
  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

📦 Module System - Giải quyết vấn đề gì?

⚠️ Vấn đề trước khi có Modules:

  • Global Namespace Pollution: Tất cả variables đều global
  • Dependency Management: Khó quản lý dependencies
  • Code Organization: Không có cách tổ chức code rõ ràng
  • Name Conflicts: Xung đột tên variables/functions
  • Loading Order: Phải load scripts theo thứ tự đúng

✅ Modules giải quyết như thế nào:

  • Separate Scope: Mỗi module có scope riêng
  • Explicit Dependencies: Import/export rõ ràng
  • Code Splitting: Chia code thành files nhỏ
  • Lazy Loading: Load code khi cần thiết
  • Tree Shaking: Loại bỏ code không dùng
  • Better Tooling: Support từ bundlers

🏛️ Module Patterns qua các thời kỳ

Module Patterns Evolution
javascript
// 🕰️ 1. IIFE Pattern (Immediately Invoked Function Expression)
var MyModule = (function() {
  var privateVar = "I'm private";
  
  function privateMethod() {
    console.log(privateVar);
  }
  
  return {
    publicMethod: function() {
      privateMethod();
    }
  };
})();

// 🕰️ 2. CommonJS (Node.js)
// math.js
function add(a, b) { return a + b; }
module.exports = { add };

// app.js
const { add } = require('./math');

// 🕰️ 3. AMD (Asynchronous Module Definition)
define(['./math'], function(math) {
  return {
    calculate: function(a, b) {
      return math.add(a, b);
    }
  };
});

// 🚀 4. ES6 Modules (ECMAScript 2015)
// math.js
export function add(a, b) { return a + b; }

// app.js
import { add } from './math.js';

🎯 Tại sao cần Classes và Modules?

  • Code Organization: Tổ chức code thành các đơn vị logic
  • Reusability: Tái sử dụng code dễ dàng
  • Maintainability: Dễ bảo trì và mở rộng
  • Encapsulation: Đóng gói dữ liệu và methods
  • Modularity: Chia nhỏ ứng dụng thành modules
  • Namespace: Tránh xung đột tên biến global
  • Scalability: Hỗ trợ xây dựng ứng dụng lớn
  • Team Collaboration: Nhiều người làm việc trên cùng dự án

📊 So sánh Classes vs Functions vs Objects

Aspect Functions Objects Classes
Purpose Logic execution Data grouping Blueprint for objects
Instantiation Call function Object literal new ClassName()
Inheritance No Prototype chain extends keyword
Private members Closures Symbols/_prefix # prefix (ES2022)
Use cases Utilities, helpers Configuration, data Complex entities

🔗 Classes và Modules - Mối quan hệ

🤝 Hoạt động cùng nhau

  • Classes định nghĩa structure
  • Modules tổ chức và phân phối
  • Một module có thể chứa nhiều classes
  • Một class có thể sử dụng nhiều modules

🎯 Best Combination

  • 1 class chính per module
  • Related classes trong cùng module
  • Utilities và helpers riêng module
  • Clear dependencies giữa modules
Classes + Modules Integration Example
javascript
// 📁 models/User.js - Class trong Module
export default class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
  
  getProfile() {
    return { name: this.name, email: this.email };
  }
}

// 📁 services/UserService.js - Service Module sử dụng Class
import User from '../models/User.js';
import { validateEmail } from '../utils/validation.js';

export class UserService {
  static users = [];
  
  static createUser(name, email) {
    if (!validateEmail(email)) {
      throw new Error('Invalid email');
    }
    
    const user = new User(name, email);
    this.users.push(user);
    return user;
  }
  
  static getAllUsers() {
    return this.users;
  }
}

// 📁 app.js - Sử dụng cả Classes và Modules
import User from './models/User.js';
import { UserService } from './services/UserService.js';

// Sử dụng Class trực tiếp
const user1 = new User('John', 'john@example.com');

// Sử dụng qua Service
const user2 = UserService.createUser('Jane', 'jane@example.com');

console.log(UserService.getAllUsers());

🏗️ JavaScript Classes - Deep Dive

🧠 Khái niệm Class trong Programming

🎯 Class là gì?

Class là một template/blueprint (bản thiết kế) để tạo ra objects. Nó định nghĩa:

  • Properties: Các thuộc tính mà object sẽ có
  • Methods: Các hành vi/chức năng mà object có thể thực hiện
  • Constructor: Cách khởi tạo object
  • State: Trạng thái của object tại một thời điểm

🏗️ Class vs Object

Class: Bản thiết kế (như bản vẽ nhà)

  • Định nghĩa structure
  • Không thể sử dụng trực tiếp
  • Template để tạo objects

Object: Thực thể cụ thể (như ngôi nhà thật)

  • Instance của class
  • Có data và behavior cụ thể
  • Có thể tương tác được

💭 Tư duy OOP

Thế giới thực → Lập trình:

  • Người: Class Person
  • John, Jane: Objects/Instances
  • Tên, tuổi: Properties
  • Nói, đi: Methods
  • Sinh viên, giáo viên: Inheritance

🔍 Cấu trúc của Class

Anatomy of a Class
javascript
class ClassName {
  // 1. PROPERTIES (Thuộc tính)
  // - Instance properties: khác nhau cho mỗi object
  // - Static properties: thuộc về class, chung cho tất cả
  
  // 2. CONSTRUCTOR (Hàm khởi tạo)
  constructor(parameters) {
    // Khởi tạo instance properties
    this.property = value;
  }
  
  // 3. METHODS (Phương thức)
  // - Instance methods: gọi từ object
  // - Static methods: gọi từ class
  // - Getters/Setters: truy cập như properties
  
  // 4. PRIVATE FIELDS (ES2022)
  // - Chỉ truy cập được từ bên trong class
  #privateProperty;
  
  #privateMethod() {
    // Private method
  }
}

🎭 Instance vs Static Members

Aspect Instance Members Static Members
Ownership Thuộc về object (instance) Thuộc về class
Access object.method() ClassName.method()
this context Refers to instance Refers to class
Memory Copy cho mỗi instance Shared, chỉ 1 copy
Use cases Object behavior, state Utilities, factory methods

🔐 Access Control trong JavaScript

🌍 Public

Truy cập từ mọi nơi

this.property
method()

🔒 Private

Chỉ trong class

this.#property
#method()

🛡️ Protected

Convention: _property

this._property
_method()

🎯 Classes là gì trong JavaScript?

Classes trong JavaScript là syntactic sugar cho prototype-based inheritance. Chúng cung cấp cách viết code OOP (Object-Oriented Programming) dễ đọc và dễ hiểu hơn, nhưng bên dưới vẫn sử dụng prototypes.

📝 Class Basics - Cú pháp cơ bản

Basic Class Declaration
javascript
// ✅ Class declaration cơ bản
class Person {
  // Constructor - hàm khởi tạo
  constructor(name, age) {
    this.name = name;
    this.age = age;
    this.id = Date.now(); // Tự động generate ID
  }
  
  // Instance methods - phương thức của đối tượng
  greet() {
    return `Hello, my name is ${this.name}`;
  }
  
  getAge() {
    return this.age;
  }
  
  setAge(newAge) {
    if (newAge > 0 && newAge < 150) {
      this.age = newAge;
    } else {
      throw new Error('Invalid age');
    }
  }
  
  // Getter - truy cập như property
  get info() {
    return `${this.name} (${this.age} years old)`;
  }
  
  // Setter - set giá trị như property
  set fullName(value) {
    const [first, last] = value.split(' ');
    this.firstName = first;
    this.lastName = last;
  }
}

// 🎯 Sử dụng class
const person1 = new Person('John Doe', 25);
const person2 = new Person('Jane Smith', 30);

console.log(person1.greet()); // "Hello, my name is John Doe"
console.log(person1.info);    // "John Doe (25 years old)" - getter
person1.fullName = 'John Smith'; // setter
person1.setAge(26);           // method call

// 🔍 Kiểm tra instance
console.log(person1 instanceof Person); // true
console.log(typeof person1);             // "object"

🔧 Class Features nâng cao

Advanced Class Features
javascript
class BankAccount {
  // 🔒 Private fields (ES2022) - bắt đầu với #
  #balance = 0;
  #accountNumber;
  
  // 📊 Static properties - thuộc về class, không phải instance
  static totalAccounts = 0;
  static bankName = 'JS Bank';
  
  constructor(initialBalance = 0) {
    this.#balance = initialBalance;
    this.#accountNumber = this.#generateAccountNumber();
    this.createdAt = new Date();
    
    // Tăng số lượng accounts
    BankAccount.totalAccounts++;
  }
  
  // 🔒 Private method
  #generateAccountNumber() {
    return Math.random().toString(36).substr(2, 9).toUpperCase();
  }
  
  // Public methods
  deposit(amount) {
    if (amount <= 0) {
      throw new Error('Amount must be positive');
    }
    this.#balance += amount;
    return this.#balance;
  }
  
  withdraw(amount) {
    if (amount <= 0) {
      throw new Error('Amount must be positive');
    }
    if (amount > this.#balance) {
      throw new Error('Insufficient funds');
    }
    this.#balance -= amount;
    return this.#balance;
  }
  
  // Getter cho private field
  get balance() {
    return this.#balance;
  }
  
  get accountNumber() {
    return this.#accountNumber;
  }
  
  // 🏭 Static methods - gọi trực tiếp từ class
  static createSavingsAccount(initialBalance) {
    const account = new BankAccount(initialBalance);
    account.type = 'savings';
    account.interestRate = 0.02;
    return account;
  }
  
  static getTotalAccounts() {
    return `${BankAccount.bankName} has ${BankAccount.totalAccounts} accounts`;
  }
}

// 🎯 Sử dụng
const account1 = new BankAccount(1000);
const account2 = BankAccount.createSavingsAccount(5000);

console.log(account1.balance);      // 1000
console.log(account1.accountNumber); // "XYZ123ABC"

account1.deposit(500);
console.log(account1.balance);      // 1500

// ❌ Không thể truy cập private fields
// console.log(account1.#balance);  // SyntaxError

// 🏭 Static methods
console.log(BankAccount.getTotalAccounts()); // "JS Bank has 2 accounts"

👨‍👩‍👧‍👦 Class Inheritance - Kế thừa

Class Inheritance with extends
javascript
// 👨‍🎓 Base class (parent)
class Employee {
  constructor(name, salary) {
    this.name = name;
    this.salary = salary;
    this.startDate = new Date();
  }
  
  work() {
    return `${this.name} is working`;
  }
  
  getAnnualSalary() {
    return this.salary * 12;
  }
  
  getInfo() {
    return `Employee: ${this.name}, Salary: $${this.salary}`;
  }
}

// 👨‍💻 Derived class (child) - kế thừa từ Employee
class Developer extends Employee {
  constructor(name, salary, programmingLanguage) {
    // Gọi constructor của parent class
    super(name, salary);
    this.programmingLanguage = programmingLanguage;
    this.projects = [];
  }
  
  // Override method từ parent
  work() {
    return `${this.name} is coding in ${this.programmingLanguage}`;
  }
  
  // Method riêng của Developer
  addProject(project) {
    this.projects.push(project);
  }
  
  // Override getInfo với thêm thông tin
  getInfo() {
    const baseInfo = super.getInfo(); // Gọi method của parent
    return `${baseInfo}, Language: ${this.programmingLanguage}`;
  }
}

// 👨‍💼 Another derived class
class Manager extends Employee {
  constructor(name, salary, department) {
    super(name, salary);
    this.department = department;
    this.team = [];
  }
  
  work() {
    return `${this.name} is managing ${this.department} department`;
  }
  
  addTeamMember(employee) {
    this.team.push(employee);
  }
  
  getTeamSize() {
    return this.team.length;
  }
  
  getInfo() {
    const baseInfo = super.getInfo();
    return `${baseInfo}, Department: ${this.department}, Team: ${this.getTeamSize()}`;
  }
}

// 🎯 Sử dụng inheritance
const dev = new Developer('Alice Johnson', 75000, 'JavaScript');
const manager = new Manager('Bob Smith', 90000, 'Engineering');

console.log(dev.work());     // "Alice Johnson is coding in JavaScript"
console.log(manager.work()); // "Bob Smith is managing Engineering department"

dev.addProject('E-commerce website');
manager.addTeamMember(dev);

console.log(dev.getInfo());
// "Employee: Alice Johnson, Salary: $75000, Language: JavaScript"

console.log(manager.getInfo());
// "Employee: Bob Smith, Salary: $90000, Department: Engineering, Team: 1"

// 🔍 Kiểm tra inheritance
console.log(dev instanceof Developer); // true
console.log(dev instanceof Employee);  // true
console.log(dev instanceof Object);    // true

🎮 Classes Demo:

Class demo output sẽ hiển thị ở đây...

📦 JavaScript Modules - Deep Dive

🧠 Khái niệm Module trong Programming

🎯 Module là gì?

Module là một đơn vị code độc lập, có thể tái sử dụng, chứa:

  • Functions: Các chức năng cụ thể
  • Classes: Định nghĩa objects
  • Variables: Data và constants
  • Configuration: Settings và config

Mỗi module có scope riênginterface rõ ràng (export/import).

🏗️ Module Design Principles

🎯 Single Responsibility

Mỗi module chỉ làm một việc và làm tốt việc đó.

  • math.js → Math operations
  • user.js → User management
  • api.js → API calls

🔗 Loose Coupling

Modules ít phụ thuộc lẫn nhau, dễ thay đổi.

  • Clear interfaces
  • Minimal dependencies
  • Independent testing

🧩 High Cohesion

Code trong module liên quan chặt chẽ với nhau.

  • Related functionality
  • Shared purpose
  • Logical grouping

📦 Encapsulation

Ẩn implementation details, chỉ expose cần thiết.

  • Private functions
  • Public API
  • Clean interface

🔄 Module Loading Strategies

Strategy When How Benefits
Static Import Build time import statement Tree shaking, optimization
Dynamic Import Runtime import() function Code splitting, lazy loading
Conditional Import Based on conditions if + import() Feature-based loading
Lazy Import When needed Event-driven import() Better initial performance

📊 Export Patterns

Export Patterns Overview
javascript
// 📤 1. NAMED EXPORTS - Multiple exports
export const API_URL = 'https://api.example.com';
export function get(url) { /* ... */ }
export function post(url, data) { /* ... */ }
export class ApiClient { /* ... */ }

// 📤 2. DEFAULT EXPORT - Main export
export default class User {
  constructor(name) { this.name = name; }
}

// 📤 3. MIXED EXPORTS - Default + Named
export default function calculate(a, b) { return a + b; }
export const PI = 3.14159;
export const E = 2.71828;

// 📤 4. RE-EXPORTS - Barrel pattern
export { User } from './User.js';
export { Product } from './Product.js';
export { Order } from './Order.js';

// 📤 5. DYNAMIC EXPORTS - Conditional
const utils = {};
if (process.env.NODE_ENV === 'development') {
  utils.debug = function() { /* debug code */ };
}
export default utils;

📥 Import Patterns

Import Patterns Overview
javascript
// 📥 1. NAMED IMPORTS - Specific items
import { get, post, API_URL } from './api.js';

// 📥 2. DEFAULT IMPORT - Main item
import User from './User.js';

// 📥 3. NAMESPACE IMPORT - All as object
import * as API from './api.js';
// Usage: API.get(), API.post(), API.API_URL

// 📥 4. MIXED IMPORT - Default + Named
import User, { USER_ROLES, createUser } from './User.js';

// 📥 5. ALIASED IMPORT - Rename imports
import { get as apiGet, post as apiPost } from './api.js';

// 📥 6. SIDE-EFFECT IMPORT - Just execute
import './polyfills.js';  // No variables imported

// 📥 7. DYNAMIC IMPORT - Runtime loading
const userModule = await import('./User.js');
const User = userModule.default;

// 📥 8. CONDITIONAL IMPORT
if (window.innerWidth < 768) {
  const mobileUtils = await import('./mobile-utils.js');
  mobileUtils.setupMobileUI();
}

🏛️ Module Architecture Patterns

🌟 Facade Pattern

Single entry point che giấu complexity

  • index.js as main entry
  • Re-export submodules
  • Simplified interface

🏭 Factory Pattern

Module exports factory functions

  • createUser(), createProduct()
  • Configuration-based creation
  • Flexible instantiation

🔌 Plugin Pattern

Extensible module system

  • Core + plugins
  • Dynamic feature loading
  • Modular architecture

🏪 Service Pattern

Business logic trong services

  • UserService, ProductService
  • Stateless operations
  • Dependency injection

🎯 Tại sao Modules quan trọng?

Modules cho phép chia code thành các file riêng biệt, mỗi file có scope riêng. Điều này giúp:

  • Maintainability: Code dễ maintain và debug
  • Reusability: Tái sử dụng modules across projects
  • Testing: Test từng module độc lập
  • Team Work: Nhiều người làm việc parallel
  • Performance: Code splitting và lazy loading
  • Dependencies: Quản lý dependencies rõ ràng

📤📥 Export và Import

mathUtils.js - Named Exports
javascript
// mathUtils.js - Module xuất nhiều functions
export const PI = 3.14159;

export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

export function multiply(a, b) {
  return a * b;
}

export function divide(a, b) {
  if (b === 0) throw new Error('Division by zero');
  return a / b;
}

// Export class
export class Calculator {
  constructor() {
    this.history = [];
  }
  
  calculate(operation, a, b) {
    let result;
    switch(operation) {
      case 'add': result = add(a, b); break;
      case 'subtract': result = subtract(a, b); break;
      case 'multiply': result = multiply(a, b); break;
      case 'divide': result = divide(a, b); break;
      default: throw new Error('Unknown operation');
    }
    
    this.history.push({operation, a, b, result});
    return result;
  }
  
  getHistory() {
    return this.history;
  }
}

// Export object
export const mathConstants = {
  PI: 3.14159,
  E: 2.71828,
  GOLDEN_RATIO: 1.61803
};
main.js - Import và sử dụng
javascript
// main.js - Các cách import

// 1. Named imports - import specific exports
import { add, subtract, PI } from './mathUtils.js';

console.log(add(5, 3));        // 8
console.log(subtract(10, 4));  // 6
console.log(PI);               // 3.14159

// 2. Import with alias - đổi tên
import { multiply as mul, divide as div } from './mathUtils.js';

console.log(mul(4, 5));  // 20
console.log(div(20, 4)); // 5

// 3. Import all - import tất cả thành namespace
import * as MathUtils from './mathUtils.js';

console.log(MathUtils.add(1, 2));     // 3
console.log(MathUtils.PI);            // 3.14159
const calc = new MathUtils.Calculator();

// 4. Import class
import { Calculator } from './mathUtils.js';

const calculator = new Calculator();
calculator.calculate('add', 10, 5);
calculator.calculate('multiply', 3, 4);
console.log(calculator.getHistory());

// 5. Mixed imports
import { mathConstants, Calculator as Calc } from './mathUtils.js';

console.log(mathConstants.E);         // 2.71828
const myCalc = new Calc();

🎯 Default Exports

User.js - Default Export
javascript
// User.js - Default export với class
export default class User {
  constructor(name, email) {
    this.name = name;
    this.email = email;
    this.id = this.generateId();
    this.createdAt = new Date();
  }
  
  generateId() {
    return Math.random().toString(36).substr(2, 9);
  }
  
  getProfile() {
    return {
      id: this.id,
      name: this.name,
      email: this.email,
      memberSince: this.createdAt.getFullYear()
    };
  }
  
  updateEmail(newEmail) {
    if (this.isValidEmail(newEmail)) {
      this.email = newEmail;
      return true;
    }
    return false;
  }
  
  isValidEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }
}

// Named exports cùng với default
export const USER_ROLES = {
  ADMIN: 'admin',
  USER: 'user',
  MODERATOR: 'moderator'
};

export function createUser(name, email, role = USER_ROLES.USER) {
  const user = new User(name, email);
  user.role = role;
  return user;
}
app.js - Import default và named
javascript
// app.js - Import default và named exports

// Import default - có thể đặt tên bất kỳ
import User from './User.js';
import MyUser from './User.js';  // Cùng module, tên khác

// Import default + named trong cùng 1 dòng
import User, { USER_ROLES, createUser } from './User.js';

// Sử dụng
const user1 = new User('John Doe', 'john@example.com');
const user2 = createUser('Jane Smith', 'jane@example.com', USER_ROLES.ADMIN);

console.log(user1.getProfile());
console.log(user2.role); // 'admin'

// ✅ Default export examples
// config.js
export default {
  apiUrl: 'https://api.example.com',
  timeout: 5000,
  retries: 3
};

// utils.js  
export default function formatDate(date) {
  return date.toLocaleDateString('vi-VN');
}

// constants.js
export default Object.freeze({
  MAX_FILE_SIZE: 5 * 1024 * 1024, // 5MB
  ALLOWED_TYPES: ['image/jpeg', 'image/png'],
  DEFAULT_LANGUAGE: 'vi'
});

⚡ Dynamic Imports

Dynamic Import Examples
javascript
// Dynamic imports - import modules khi cần thiết

// 1. Basic dynamic import
async function loadMathUtils() {
  try {
    const mathModule = await import('./mathUtils.js');
    
    // Sử dụng named exports
    console.log(mathModule.add(5, 3));
    console.log(mathModule.PI);
    
    // Sử dụng class
    const calc = new mathModule.Calculator();
    return calc;
  } catch (error) {
    console.error('Failed to load math utils:', error);
  }
}

// 2. Conditional loading
async function loadUserModule(userType) {
  let UserClass;
  
  if (userType === 'admin') {
    const module = await import('./AdminUser.js');
    UserClass = module.default;
  } else {
    const module = await import('./RegularUser.js');
    UserClass = module.default;
  }
  
  return new UserClass();
}

// 3. Lazy loading với destructuring
async function calculateAdvanced(operation, a, b) {
  // Chỉ load module khi cần advanced calculations
  const { advancedMath } = await import('./advancedMath.js');
  
  return advancedMath[operation](a, b);
}

// 4. Dynamic import với Promise.all
async function loadMultipleModules() {
  try {
    const [mathModule, userModule, utilsModule] = await Promise.all([
      import('./mathUtils.js'),
      import('./User.js'),
      import('./utils.js')
    ]);
    
    return {
      math: mathModule,
      User: userModule.default,
      utils: utilsModule
    };
  } catch (error) {
    console.error('Failed to load modules:', error);
  }
}

// 5. Dynamic import với error handling
function loadModuleWithRetry(modulePath, maxRetries = 3) {
  return new Promise(async (resolve, reject) => {
    for (let i = 0; i < maxRetries; i++) {
      try {
        const module = await import(modulePath);
        resolve(module);
        return;
      } catch (error) {
        if (i === maxRetries - 1) {
          reject(error);
        }
        // Wait before retry
        await new Promise(r => setTimeout(r, 1000 * (i + 1)));
      }
    }
  });
}

// 6. Feature detection + dynamic import
async function loadFeatureModule(featureName) {
  // Kiểm tra browser support
  if ('serviceWorker' in navigator && featureName === 'offline') {
    const module = await import('./offlineFeature.js');
    return module.default;
  }
  
  if ('geolocation' in navigator && featureName === 'location') {
    const module = await import('./locationFeature.js');
    return module.default;
  }
  
  throw new Error(`Feature ${featureName} not supported`);
}

✨ Lợi ích của Dynamic Imports:

  • Code Splitting: Chia nhỏ bundle thành chunks
  • Lazy Loading: Load code khi cần thiết
  • Conditional Loading: Load module theo điều kiện
  • Performance: Giảm initial bundle size
  • Feature Detection: Load polyfills khi cần

✨ Best Practices

🎯 Classes Best Practices:

  • Single Responsibility: Mỗi class chỉ làm 1 việc
  • Encapsulation: Sử dụng private fields (#) cho internal state
  • Composition over Inheritance: Ưu tiên composition
  • Immutable objects: Không modify objects sau khi tạo
  • Validation: Validate input trong constructor và setters

📦 Modules Best Practices:

  • Clear naming: Tên file và exports rõ ràng
  • Single purpose: Mỗi module có 1 mục đích cụ thể
  • Default vs Named: Dùng default cho main export
  • Barrel exports: Re-export từ index.js
  • Avoid circular dependencies: Tránh import lẫn nhau
Real-world Example - E-commerce System
javascript
// Product.js - Product class
export default class Product {
  #price;
  #stock;
  
  constructor(name, price, category) {
    this.id = this.generateId();
    this.name = name;
    this.#price = price;
    this.category = category;
    this.#stock = 0;
    this.createdAt = new Date();
  }
  
  generateId() {
    return `prod_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
  }
  
  get price() { return this.#price; }
  get stock() { return this.#stock; }
  
  setPrice(newPrice) {
    if (newPrice < 0) throw new Error('Price cannot be negative');
    this.#price = newPrice;
  }
  
  addStock(quantity) {
    if (quantity < 0) throw new Error('Quantity cannot be negative');
    this.#stock += quantity;
  }
  
  reduceStock(quantity) {
    if (quantity > this.#stock) throw new Error('Insufficient stock');
    this.#stock -= quantity;
  }
}

// Cart.js - Shopping cart
import Product from './Product.js';

export default class ShoppingCart {
  constructor() {
    this.items = new Map();
    this.discounts = [];
  }
  
  addItem(product, quantity = 1) {
    if (!(product instanceof Product)) {
      throw new Error('Invalid product');
    }
    
    if (product.stock < quantity) {
      throw new Error('Insufficient stock');
    }
    
    const existingQuantity = this.items.get(product.id) || 0;
    this.items.set(product.id, existingQuantity + quantity);
    
    // Reduce stock
    product.reduceStock(quantity);
  }
  
  removeItem(productId) {
    this.items.delete(productId);
  }
  
  getTotal() {
    let total = 0;
    this.items.forEach((quantity, productId) => {
      const product = this.findProduct(productId);
      if (product) {
        total += product.price * quantity;
      }
    });
    
    return this.applyDiscounts(total);
  }
  
  applyDiscounts(total) {
    return this.discounts.reduce((acc, discount) => {
      return discount.apply(acc);
    }, total);
  }
}

// app.js - Main application
async function main() {
  // Dynamic import cho admin features
  const isAdmin = await checkUserRole();
  
  if (isAdmin) {
    const { AdminPanel } = await import('./admin/AdminPanel.js');
    const adminPanel = new AdminPanel();
    adminPanel.init();
  }
  
  // Regular e-commerce functionality
  const { default: Product } = await import('./Product.js');
  const { default: ShoppingCart } = await import('./Cart.js');
  
  const laptop = new Product('MacBook Pro', 2000, 'Electronics');
  const cart = new ShoppingCart();
  
  cart.addItem(laptop, 1);
  console.log('Total:', cart.getTotal());
}

💪 Bài tập thực hành

Dễ

🏗️ Bài 1: Library Management System

Yêu cầu: Tạo hệ thống quản lý thư viện với classes Book, Author, và Library.

🎯 Yêu cầu chi tiết:

  • Class Author: name, birthYear, nationality
  • Class Book: title, author (Author object), isbn, year
  • Class Library: books[], addBook(), removeBook(), findBooks()
  • Implement search by title, author, year
  • Add validation cho ISBN format
Trung bình

📦 Bài 2: Modular Calculator System

Yêu cầu: Tạo hệ thống calculator sử dụng modules, với các file riêng cho basic operations, advanced operations, và UI.

🎯 Yêu cầu chi tiết:

  • basicMath.js: add, subtract, multiply, divide
  • advancedMath.js: power, sqrt, sin, cos, log
  • Calculator.js: Main calculator class với history
  • ui.js: UI handling và event listeners
  • app.js: Main app với dynamic imports
  • Load advanced math chỉ khi cần thiết
Khó

🏪 Bài 3: E-commerce Platform

Yêu cầu: Tạo platform e-commerce hoàn chỉnh với inheritance, modules, và dynamic loading.

🎯 Yêu cầu chi tiết:

  • Base classes: User, Product, Order
  • Inherited classes: Customer, Seller, Admin extends User
  • Product types: PhysicalProduct, DigitalProduct
  • Modules: cart, payment, shipping, inventory
  • Features: Private fields, static methods, getters/setters
  • Dynamic imports: Load payment modules conditionally
  • Error handling: Custom error classes